package com.amigo.online.provider.user.mongo.dao.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import com.amigo.online.common.param.dto.user.ProbePeopleDto;
import com.amigo.online.provider.user.common.ConstantValue;
import com.amigo.online.provider.user.mongo.dao.UserDao;
import com.amigo.online.provider.user.mongo.entity.UserEntity;
import com.mongodb.BasicDBObject;
import com.mongodb.client.AggregateIterable;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.result.UpdateResult;

@Component
public class UserDaoImpl implements UserDao {

    protected static final Logger logger = LoggerFactory.getLogger(UserDaoImpl.class);

    @Autowired
    @Qualifier("readMongoTemplate")
    MongoTemplate readMongoTemplate;

    @Autowired
    @Qualifier("writeMongoTemplate")
    MongoTemplate writeMongoTemplate;

    @Override
    public void saveUser(UserEntity user) {
        writeMongoTemplate.save(user);
    }

    @Override
    public Long updateUser(UserEntity user) {
        Query query = new Query(Criteria.where("id").is(user.getId()));
        Update update = new Update().set("nickname", user.getNickname()).set("sex", user.getSex())
                .set("avatar", user.getAvatar()).set("birthday", user.getBirthday()).set("job", user.getJob())
                .set("vip", user.getVip()).set("celebrity", user.getCelebrity()).set("verified", user.getVerified())
                .set("sign", user.getSign()).set("loginTime", System.currentTimeMillis() / 1000)
                .set("platform", user.getPlatform()).set("loc", user.getLoc()).set("online", user.getOnline())
                .set("version", user.getVersion()).set("device", user.getDevice()).set("firstImg", user.getFirstImg())
                .set("openT", user.getOpenT()).set("checked", user.getChecked()).set("secret", user.getSecret());

        UpdateResult result = writeMongoTemplate.updateFirst(query, update, UserEntity.class);
        if (result != null) {
            return result.getModifiedCount();
        } else {
            return 0L;
        }
    }

    @Override
    public void deleteUserById(Long id) {
        Query query = new Query(Criteria.where("id").is(id));
        writeMongoTemplate.remove(query, UserEntity.class);
    }

    @Override
    public UserEntity getUserById(Long id) {
        Query query = new Query(Criteria.where("id").is(id));
        return readMongoTemplate.findOne(query, UserEntity.class);
    }

    @Override
    public AggregateIterable<Document> findAroundPeople(String collection, Double[] point, int distance,
                                                        BasicDBObject query, List<BasicDBObject> pipeline) {
        BasicDBObject aggregate = new BasicDBObject("$geoNear",
                new BasicDBObject("near", new BasicDBObject("type", "Point").append("coordinates", point))
                        .append("distanceField", "distance").append("query", query)
                        .append("limit", ConstantValue.MAX_SHOW_NUMBER).append("maxDistance", distance)
                        .append("spherical", true));
        pipeline.add(0, aggregate);
        JSONObject.toJSONString(pipeline, SerializerFeature.WriteMapNullValue);
        AggregateIterable<Document> iterators = readMongoTemplate.getCollection(collection).aggregate(pipeline);
        return iterators;
    }

    @Override
    public List<UserEntity> getUsersByIds(List<Long> ids) {
        Query query = new Query(Criteria.where("id").in(ids));
        return readMongoTemplate.find(query, UserEntity.class);
    }

    @Override
    public AggregateIterable<Document> findLocationById(List<Long> ids, String collection,
                                                        List<BasicDBObject> pipeline) {

        AggregateIterable<Document> iterators = readMongoTemplate.getCollection(collection).aggregate(pipeline);
        return iterators;
    }

    @Override
    public long updateUserInfo(Query query, Update update) {
        update.set("loginTime", System.currentTimeMillis() / 1000);
        UpdateResult result = writeMongoTemplate.updateFirst(query, update, UserEntity.class);
        if (result != null) {
            return result.getModifiedCount();
        } else {
            return 0L;
        }
    }

    /**
     * 获取探探用户
     * @param probePeopleDto
     * @param userId
     * @param idList
     * @param likeMe
     * @return
     */
    @Override
    public MongoCursor<Document> probePeople(ProbePeopleDto probePeopleDto, Long userId, Set<Long> idList, int likeSize) {
        long currenttime = System.currentTimeMillis() / 1000; // 当前秒数
        BasicDBObject obQuery = new BasicDBObject();
        // 过滤到传过来的数组
        obQuery.put("_id", new BasicDBObject("$not", new BasicDBObject("$in", idList.toArray())));
        // 查询已开通并审核通过的人
        obQuery.put("openT", 1);
        obQuery.put("checked", 1);
        // 性别条件
        Integer sexType = probePeopleDto.getSexType();
        if (sexType != null) {
            if (sexType.equals(2)) { // 男
                obQuery.append("sex", "M");
            } else if (sexType.equals(3)) { // 女
                obQuery.append("sex", "F");
            } else if (sexType.equals(4)) { // 未知
                obQuery.append("sex", "UN");
            }
        }
        // 年龄条件
        long starttime = 0L;
        Integer leastAge = probePeopleDto.getLeastAge();
        if (null == leastAge) {
            starttime = ConstantValue.LEAST_AGE * ConstantValue.YEAR_MILLIS;
        } else {
            starttime = leastAge * ConstantValue.YEAR_MILLIS;
        }
        BasicDBObject birthday = new BasicDBObject("$lte", currenttime - starttime);
        Integer largetAge = probePeopleDto.getLargestAge();
        if (null != largetAge) {
            long endtime = currenttime - largetAge * ConstantValue.YEAR_MILLIS;
            birthday.put("$gte", endtime);
        }
        obQuery.put("birthday", birthday);
        BasicDBObject version = new BasicDBObject();
        version.put("$ne",null);
        obQuery.put("version",version);
        List<BasicDBObject> pipeline = new ArrayList<>();
        List<Object> birdiv = new ArrayList<>();
        List<Object> birsub = new ArrayList<>();
        birsub.add(currenttime);
        birsub.add("$birthday");
        birdiv.add(new BasicDBObject("$subtract", birsub));
        birdiv.add(ConstantValue.YEAR_MILLIS);
        BasicDBObject projectObject = new BasicDBObject("$project",
                new BasicDBObject("_id", 1).append("sex", 1).append("nickname", 1).append("firstImg", 1)
                        .append("sign", 1).append("loginTime", 1).append("job", 1).append("birthday", 1)
                        .append("dis", new BasicDBObject("$trunc", "$distance"))
                        .append("bir", new BasicDBObject("$trunc", new BasicDBObject("$divide", birdiv)))
                        .append("type", "$type"));
        pipeline.add(projectObject);
        // 更改逻辑
        // 随机出10个出来
        int size = (ConstantValue.RANDOM_SHOW_TANTAN - likeSize)>0 ? ConstantValue.RANDOM_SHOW_TANTAN - likeSize : 0;

        BasicDBObject randomObject = new BasicDBObject("$sample",
                new BasicDBObject("size", size));
        pipeline.add(randomObject);
        // 距离条件   当>=100  无限距离
        Double[] point = new Double[2];
        point[0] = Double.parseDouble(probePeopleDto.getLng());
        point[1] = Double.parseDouble(probePeopleDto.getLat());
        BasicDBObject dis = new BasicDBObject("near", new BasicDBObject("type", "Point").append("coordinates", point))
                .append("distanceField", "distance").append("query", obQuery);
        if (probePeopleDto.getMaxDistance() < 100) {
            dis.append("maxDistance", probePeopleDto.getMaxDistance() * 1000);
        }
        dis.append("spherical", true);
        BasicDBObject aggregate = new BasicDBObject("$geoNear", dis);
        pipeline.add(0, aggregate);
        logger.info(JSON.toJSONString(pipeline));
        //必须在loc上建立2DSphere类型索引
        return readMongoTemplate.getCollection(ConstantValue.COLLECTION_USER).aggregate(pipeline).iterator();
    }

    /**
     * 喜欢我的用户
     * @param probePeopleDto
     * @param userId
     * @param idList
     * @param likeMe
     * @return
     */
    @Override
    public MongoCursor<Document> probePeopleLikeMe(ProbePeopleDto probePeopleDto, Long userId, Set<Long> idList, List<Long> likeMe) {
        long currenttime = System.currentTimeMillis() / 1000; // 当前秒数
        BasicDBObject obQuery = new BasicDBObject();
        // 过滤到传过来的数组
        obQuery.put("_id", new BasicDBObject("$in", likeMe));
        // 查询已开通并审核通过的人
        obQuery.put("openT", 1);
        obQuery.put("checked", 1);
        // 性别条件
        Integer sexType = probePeopleDto.getSexType();
        if (sexType != null) {
            if (sexType.equals(2)) { // 男
                obQuery.append("sex", "M");
            } else if (sexType.equals(3)) { // 女
                obQuery.append("sex", "F");
            } else if (sexType.equals(4)) { // 未知
                obQuery.append("sex", "UN");
            }
        }
        // 年龄条件
        long starttime = 0L;
        Integer leastAge = probePeopleDto.getLeastAge();
        if (null == leastAge) {
            starttime = ConstantValue.LEAST_AGE * ConstantValue.YEAR_MILLIS;
        } else {
            starttime = leastAge * ConstantValue.YEAR_MILLIS;
        }
        BasicDBObject birthday = new BasicDBObject("$lte", currenttime - starttime);
        Integer largetAge = probePeopleDto.getLargestAge();
        if (null != largetAge) {
            long endtime = currenttime - largetAge * ConstantValue.YEAR_MILLIS;
            birthday.put("$gte", endtime);
        }
        obQuery.put("birthday", birthday);
        BasicDBObject version = new BasicDBObject();
        version.put("$ne",null);
        obQuery.put("version",version);
        List<BasicDBObject> pipeline = new ArrayList<>();
        List<Object> birdiv = new ArrayList<>();
        List<Object> birsub = new ArrayList<>();
        birsub.add(currenttime);
        birsub.add("$birthday");
        birdiv.add(new BasicDBObject("$subtract", birsub));
        birdiv.add(ConstantValue.YEAR_MILLIS);
        BasicDBObject projectObject = new BasicDBObject("$project",
                new BasicDBObject("_id", 1).append("sex", 1).append("nickname", 1).append("firstImg", 1)
                        .append("sign", 1).append("loginTime", 1).append("job", 1).append("birthday", 1)
                        .append("dis", new BasicDBObject("$trunc", "$distance"))
                        .append("bir", new BasicDBObject("$trunc", new BasicDBObject("$divide", birdiv)))
                        .append("type", "$type"));
        pipeline.add(projectObject);
        // 更改逻辑
        // 随机出10个出来
        int size = (ConstantValue.RANDOM_SHOW_TANTAN - likeMe.size())>0 ? ConstantValue.RANDOM_SHOW_TANTAN - likeMe.size() : 0;

        BasicDBObject randomObject = new BasicDBObject("$sample",
                new BasicDBObject("size", size));
        pipeline.add(randomObject);
        // 距离条件   当>=100  无限距离
        Double[] point = new Double[2];
        point[0] = Double.parseDouble(probePeopleDto.getLng());
        point[1] = Double.parseDouble(probePeopleDto.getLat());
        BasicDBObject dis = new BasicDBObject("near", new BasicDBObject("type", "Point").append("coordinates", point))
                .append("distanceField", "distance").append("query", obQuery);
        if (probePeopleDto.getMaxDistance() < 100) {
            dis.append("maxDistance", probePeopleDto.getMaxDistance() * 1000);
        }
        dis.append("spherical", true);
        BasicDBObject aggregate = new BasicDBObject("$geoNear", dis);
        pipeline.add(0, aggregate);
        //必须在loc上建立2DSphere类型索引
        return readMongoTemplate.getCollection(ConstantValue.COLLECTION_USER).aggregate(pipeline).iterator();
    }
    @Override
    public Long updateLastLoginTime(UserEntity user) {
        Query query = new Query(Criteria.where("id").is(user.getId()));
        Update update = new Update().set("loginTime", user.getLoginTime());
        UpdateResult result = writeMongoTemplate.updateFirst(query, update, UserEntity.class);
        if (result != null) {
            return result.getModifiedCount();
        } else {
            return 0L;
        }

    }

    /**
     * <p>Title: updateUserInfo</p>
     * <p>Description: </p>
     *
     * @param id
     * @param update
     * @return
     * @see com.amigo.online.provider.user.mongo.dao.UserDao#updateUserInfo(long, org.springframework.data.mongodb.core.query.Update)
     */
    @Override
    public long updateUserInfo(long id, Update update) {
        Query query = new Query().addCriteria(Criteria.where("_id").is(id));
        update.set("loginTime", System.currentTimeMillis() / 1000);
        UpdateResult result = writeMongoTemplate.updateFirst(query, update, UserEntity.class);
        if (result != null) {
            return result.getModifiedCount();
        } else {
            return 0L;
        }
    }

}
